package com.garbagecode.resultbounds.utilty;

import java.lang.reflect.Field;
import java.util.List;

import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlSource;

public class MappedStatementModifier {
  private static Field keyPropertiesField;
  private static Field keyColumnsField;
  private static Field resultSetsField;

  static {
    Class<?> cls = MappedStatement.class;

    try {
      keyPropertiesField = cls.getDeclaredField("keyProperties");
      keyPropertiesField.setAccessible(true);

      keyColumnsField = cls.getDeclaredField("keyColumns");
      keyColumnsField.setAccessible(true);

      resultSetsField = cls.getDeclaredField("resultSets");
      resultSetsField.setAccessible(true);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public MappedStatement modify(MappedStatement mappedStatement, 
                                SqlSource sqlSource, 
                                List<ResultMap> resultMaps) {
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(
        mappedStatement.getConfiguration(),
        mappedStatement.getId(), 
        sqlSource, 
        mappedStatement.getSqlCommandType());

    statementBuilder.resource(mappedStatement.getResource());
    statementBuilder.fetchSize(mappedStatement.getFetchSize());
    statementBuilder.statementType(mappedStatement.getStatementType());
    statementBuilder.keyGenerator(mappedStatement.getKeyGenerator());
    statementBuilder.databaseId(mappedStatement.getDatabaseId());
    statementBuilder.lang(mappedStatement.getLang());
    statementBuilder.resultOrdered(mappedStatement.isResultOrdered());

    // setStatementTimeout
    statementBuilder.timeout(mappedStatement.getTimeout());

    // setStatementParameterMap
    statementBuilder.parameterMap(mappedStatement.getParameterMap());

    // setStatementResultMap
    resultMaps = (resultMaps == null) 
        ? mappedStatement.getResultMaps() 
        : resultMaps;
    statementBuilder.resultMaps(resultMaps);

    statementBuilder.resultSetType(mappedStatement.getResultSetType());

    // setStatementCache
    statementBuilder.cache(mappedStatement.getCache());
    statementBuilder.flushCacheRequired(mappedStatement.isFlushCacheRequired());
    statementBuilder.useCache(mappedStatement.isUseCache());

    MappedStatement newMappedStatement = statementBuilder.build();

    try {
      keyPropertiesField.set(newMappedStatement, mappedStatement.getKeyProperties());
      keyColumnsField.set(newMappedStatement, mappedStatement.getKeyColumns());
      resultSetsField.set(newMappedStatement, mappedStatement.getResulSets());
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    return newMappedStatement;
  }

}
